package com.riccardocardin.structured.concurrency.util;

import com.riccardocardin.structured.concurrency.policy.ShutdownOnResult;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.StructuredTaskScope;

public class Concurrents {

    public record Pair<T1, T2>(T1 first, T2 second) {
    }

    public static <T1, T2> Pair<T1, T2> par(Callable<T1> first, Callable<T2> second)
            throws InterruptedException, ExecutionException {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            var firstTask = scope.fork(first);
            var secondTask = scope.fork(second);
            scope.join().throwIfFailed();
            return new Pair<>(firstTask.get(), secondTask.get());
        }
    }

    public static <T> T raceAll(Callable<T> first, Callable<T> second)
            throws InterruptedException, ExecutionException {
        try (var scope = new StructuredTaskScope.ShutdownOnSuccess<T>()) {
            scope.fork(first);
            scope.fork(second);
            return scope.join().result(ExecutionException::new);
        }
    }

    public static <T> T race(Callable<T> first, Callable<T> second)
            throws InterruptedException, ExecutionException {
        try (var scope = new ShutdownOnResult<T>()) {
            scope.fork(first);
            scope.fork(second);
            return scope.join().resultOrThrow();
        }
    }
}
